# -*- coding: utf-8 -*-

"""处理地形数据"""

__authur__ = 'Yu XH'

# import pandas as pd
import numpy as np

from .hex import HexOffset, HexCube
from ..const import *
# from ..attdamage.handle_shoot import *  # 计算伤害的代码，在第一版中
from .astar import astar


def mapid_2_hexoffset(loc):
    """ 2020-4-25
    把mapID（4位整数）转换成HexOffset类实例
    """
    return HexOffset(int(loc // 100), int(loc % 100))


def hexoffset_2_mapid(hex):
    """ 2020-4-25
    把HexOffset类实例转换成mapID（4位整数）
    """
    return int(hex.row * 100 + hex.col)


def cal_distance(hex1, hex2):
    """ 2020-4-18
    计算两个六角格的格距（曼哈顿距离）
    :param hex1, hex2: 4位整数的六角格坐标
    :return: hex1和hex2的距离
    """
    h1 = mapid_2_hexoffset(hex1)
    h2 = mapid_2_hexoffset(hex2)
    return h1.calc_distance(h2)


def get_direction(refer_hex, tar_hex):
    """ 2020-5-2
    确定tar_hex在refer_hex的哪个方向上，正东方向=0，顺时针++
    """
    return mapid_2_hexoffset(refer_hex).get_wedge_direction(mapid_2_hexoffset(tar_hex))


def get_hex_in_direction(refer_hex, direction, radius):
    """找出某个方向上距离为radius的六角格offset坐标"""
    refer_hex_offset = mapid_2_hexoffset(refer_hex)
    tar_hex_cube = refer_hex_offset.cvt_2_cube().cube_add(HexCube(0, 0, 0).cube_directions(direction).cub_multiply(radius))
    tar_hex = hexoffset_2_mapid(tar_hex_cube.cvt_2_offset())
    return tar_hex


def get_range(center, radius):
    """ 2020-6-10  加入局部地图的考虑
    获得以center为中心，radius为半径的圆内所有六角格。输出mapID构成的列表
    """
    c_hex = mapid_2_hexoffset(center)
    range_hexs = c_hex.get_range(radius)
    range = []
    for hex in range_hexs:
        range.append(hexoffset_2_mapid(hex))
    return range


def get_arc(center, radius, direction):
    """ 2020-5-3
    获得以center为中心，radius（含）为半径，在direction方向上的弧
    """
    arc_offset = mapid_2_hexoffset(center).get_arc(radius, direction)
    arc = []
    for hex in arc_offset:
        arc.append(hexoffset_2_mapid(hex))
    return arc


def get_sector(center, radius, direction):
    """ 2020-5-12
    获得以center为中心，radius（含）为半径，在direction方向上的扇形区域，包含center
    """
    sector_offset = []
    for r in range(1, radius + 1):
        arc = mapid_2_hexoffset(center).get_arc(r, direction)
        sector_offset += arc
    sector = []
    for hex in sector_offset:
        sector.append(hexoffset_2_mapid(hex))
    sector.insert(0, center)
    return sector


def get_line(hex_s, hex_t):
    """ 2020-4-28
    获得以hex_s为起点，hex_t为终点的直线List
    """
    line = []
    hex_s_offset, hex_t_offset = mapid_2_hexoffset(hex_s), mapid_2_hexoffset(hex_t)
    line_offset = hex_s_offset.get_line(hex_t_offset)
    for h in line_offset:
        line.append(hexoffset_2_mapid(h))
    return line


def get_neighbor_ind(map_data_hex, nei_hex):
    """ 2020-5-12
    计算nei_hex位于cur_hex的哪个方向，用于map_data标号的确定
      2/\ 1
    3|  | 0
    4\/ 5
    正东方向为0，逆时针增加（与HexCube中的方向有区别：顺时针增加）
    """
    for ind in range(6):
        if nei_hex == map_data_hex['neighbors'][ind]:
            return ind
    return None  # nei_hex不是cur_hex的邻格


def get_closest_hex(cur_hex, hex_area):
    """从六角格区域hex_area中找出距cur_hex最近的六角格"""
    return min(hex_area, key=lambda x: cal_distance(cur_hex, x))


def get_farrest_hex(cur_hex, hex_area):
    """从六角格区域hex_area中找出距cur_hex最远的六角格"""
    return min(hex_area, key=lambda x: cal_distance(cur_hex, x))


def calc_avg_distance(cur_hex, hex_area):
    """计算从六角格cur_hex到区域hex_area的平均距离"""
    if not hex_area:
        return None
    sum_distance = 0.
    n_hex = len(hex_area)
    for hex in hex_area:
        sum_distance += cal_distance(cur_hex, hex)
    return 1. * sum_distance / n_hex


def calc_max_distance(cur_hex, hex_area):
    """计算从六角格cur_hex到区域hex_area的最大距离"""
    if not hex_area:
        return None
    max_distance = 0.
    for hex in hex_area:
        curr_dist = cal_distance(cur_hex, hex)
        if max_distance < curr_dist:
            max_distance = curr_dist
    return max_distance


def get_mod(type1, type2):
    if type1 != 3 and type2 != 3:
        return 0
    elif type1 == 3 and type2 == 3:
        return 1
    elif type1 == 3 and type2 != 3:
        return 2
    elif type1 != 3 and type2 == 3:
        return 3


def get_terrain_info(map_data, cur_hex):
    """ 2020-5-12
    确定六角格的地形信息
    :param map_data: 地图信息，地图数据获取接口 def get_map_data(self)
    :param cur_hex: 六角格的MapID（4位整数）
    :return: 0-开阔地 1-丛林地 2-居民地 3-松软地 4-河流
    """
    row, col = cur_hex // 100, cur_hex % 100
    return map_data[row][col]['cond']


def get_terrain_info_matind(terrain_vec, hex_inds):
    """ 2020-5-24
    确定六角格的地形信息，hex_inds是一个mat_ind的数组
    """
    return terrain_vec[hex_inds]


def get_elev_info(map_data, cur_hex):
    """ 2020-5-12
    确定六角格的高程信息
    :param map_data: 地图信息，地图数据获取接口 def get_map_data(self)
    :param cur_hex: 六角格的MapID（4位整数）
    :return: {int} 高程数值
    """
    row, col = cur_hex // 100, cur_hex % 100
    return map_data[row][col]['elev']


def can_see_obj(a_obj, b_obj, map):
    """ 2020-6-6
    判断算子a_obj是否可以看到算子b_obj
    """
    a_loc, b_loc = a_obj['cur_hex'], b_obj['cur_hex']
    a_type, b_type = a_obj['type'], b_obj['type']
    mod = get_mod(a_type, b_type)
    if map.can_see(a_loc, b_loc, mod):
        if b_obj['type'] == BopType.Vehicle or b_obj['sub_type'] == BopType.Aircraft:
            observe_distance = a_obj['observe_distance'][ObservedType.Large]
        elif b_obj['type'] == BopType.Infantry:
            observe_distance = a_obj['observe_distance'][ObservedType.Medium]
        else:
            observe_distance = a_obj['observe_distance'][ObservedType.Little]
        if b_type != BopType.Aircraft:
            b_terrain = get_terrain_info(map.get_map_data(), b_loc)
            if b_terrain in [TerrainType.Forest, TerrainType.Resident]:  # 丛林地、居民地，视距减半
                observe_distance = observe_distance / 2.
            if b_obj['move_state'] == MoveState.Shelter:  # 处于掩蔽状态的判断
                if b_type == 1:
                    observe_distance = observe_distance / 2.
                else:  # 车辆还要考虑高程
                    a_loc_elev = get_elev_info(map.get_map_data(), a_loc)
                    b_loc_elev = get_elev_info(map.get_map_data(), b_loc)
                    if a_loc_elev <= b_loc_elev:
                        observe_distance = observe_distance / 2.
        observe_distance = int(observe_distance)
        if map.get_distance(a_loc, b_loc) <= observe_distance:
            return True
    return False


def get_road_link_level(cur_hex, nei_hex, map_data):
    """
    判断当前格是否与邻格nei_hex有什么等级的道路连接
    :param nei_hex: 邻格
    :param map_data: 地图信息，地图数据获取接口 def get_map_data(self)
    :return: 道路类型, 0-无 1-黑色 2-红色 3-黄色
    """
    map_data_hex = map_data[cur_hex // 100][cur_hex % 100]
    nei_ind = get_neighbor_ind(map_data_hex, nei_hex)
    if not nei_ind:
        raise Exception('非邻格，见get_road_link_level函数')
    else:
        return map_data_hex['roads'][nei_ind]


def calc_elev_diff(att_hex, tar_hex, map_data):
    """ 2020-5-12
    计算攻击六角格att_hex与目标六角格tar_hex的高程差
    """
    att_elev = map_data[att_hex // 100][att_hex % 100]['elev']
    tar_elev = map_data[tar_hex // 100][tar_hex % 100]['elev']
    return (att_elev - tar_elev) // 20


def calc_speed_multiplier(start_hex, end_hex, obj_type, cost_data):
    """ 2020-5-12
    计算从start_hex到它的邻格end_hex的速度乘子
    """
    if obj_type == 3:  # 空中算子
        return 1
    if obj_type == 1:  # 步兵
        cost_dict = cost_data[2][start_hex // 100][start_hex % 100]
    else:  # 车辆
        cost_dict = cost_data[0][start_hex // 100][start_hex % 100]
    if end_hex in cost_dict.keys():
        return cost_dict[end_hex]
    else:
        raise Exception("输入的2个六角格不可达")


def calc_route_time(obj, path, cost_data):
    """ 2020-5-12
    计算算子obj行驶路线path的时间消耗
    """
    total_time = 0
    cur_hex = obj['cur_hex']
    for hex in path:
        multiplier = calc_speed_multiplier(cur_hex, hex, obj['type'], cost_data)
        speed = (obj['basic_speed'] / multiplier) / 3.6
        total_time += 200 / speed
        cur_hex = hex
    return total_time


def calc_safty(cur_hex, obj_type, obj_armor, map):
    """ 2020-5-5
    计算cur_hex的安全性，不考虑该点的隐蔽性
    :return: 归一化后的安全性指标，值越大，安全性越高
    """
    armor_car_resident = [1, 0.67, 0.19, 0.08, -0.47]
    armor_car_forest_water = [0.86, 0.44, 0.08, -0.06, -0.78]
    armor_opening = [1.17, 0.92, 0.39, 0.25, -0.19]
    soldier = [0.28, 0.14, 0.42]  # 居民地、丛林地、开阔地
    max_diff = 0.59  # 丛林地形的保护，最大的效用：复合装甲，丛林期望-0.78，开阔地期望-0.19
    terrain = get_terrain_info(map.get_map_data(), cur_hex)
    if obj_type == BopType.Infantry:  # 人员
        if terrain == TerrainType.Resident:
            return (soldier[2] - soldier[0]) / max_diff  # 归一化
        elif terrain == TerrainType.Forest:
            return (soldier[2] - soldier[1]) / max_diff  # 归一化
        else:
            return 0
    elif obj_type == BopType.Vehicle:  # 车辆
        if terrain == TerrainType.Resident:
            return (armor_opening[obj_armor] - armor_car_resident[obj_armor]) / max_diff
        elif terrain == TerrainType.Forest:
            return (armor_opening[obj_armor] - armor_car_forest_water[obj_armor]) / max_diff
        else:
            return 0
    return 0


def calc_hide_ratio(obj_type, cur_hex, directions, myAgent):
    """ 2020-5-3
    计算某个类型的算子在当前位置，针对某些方向的隐蔽比例，所有格子等概率
    """
    if not directions:
        raise Exception("输入参数directions为空")
    if obj_type == 1:  # 步兵
        distance = 10
        count_each_dir = 55.  # 每个楔形方向上的格子总数：1+2+3+4+...
    else:  # 车辆
        distance = 25
        count_each_dir = 325.  # 每个楔形方向上的格子总数：1+2+3+4+...
    n_dir = len(directions)  # 需要考虑的方向数量
    unhidden_count = 0  # 考虑distance和directions后的暴露六角格计数
    tar_area = []
    for direct in directions:
        tar_area += get_sector(cur_hex, distance, direct)
    for hex in tar_area:
        if myAgent.map.can_see(cur_hex, hex, 0):
            if cal_distance(cur_hex, hex) <= distance and hex != cur_hex:
                unhidden_count += 1.
    hide_ratio = 1. - unhidden_count / (count_each_dir * n_dir)
    return hide_ratio


def calc_hide_ratio_2(obj_type, cur_hex, tar_area, map):
    """ 2020-5-12
    计算某个类型的算子在当前位置，针对目标区域tar_area的隐蔽比例，所有格子等概率
    """
    if obj_type == 1:  # 步兵
        distance = 10
    else:  # 车辆
        distance = 25
    n_targets = len(tar_area)
    unhidden_count = 0.
    for hex in tar_area:
        if map.can_see(cur_hex, hex, 0):
            if cal_distance(cur_hex, hex) <= distance and hex != cur_hex:
                unhidden_count += 1.
    hide_ratio = 1. - unhidden_count / n_targets
    return hide_ratio


def calc_unhidden_ratio(obj_type, obj_locs, tar_locs, see_matrix, dist_matrix, distance=None,
                        tar_loc_probs=np.array([])):
    """ 2020-5-19
    计算某个类型的算子在当前位置，针对目标区域tar_locs的暴露比例，所有格子等概率
    以矩阵的方式计算，所有六角格都以mat_ind表示
    obj_locs, tar_locs必须是narray类型
    :param tar_loc_probs: 每个格子的概率（权值），如果为空，表示所有格子等概率
   """
    n_obj_locs, n_tar_locs = obj_locs.size, tar_locs.size
    if not distance:
        if obj_type == BopType.Infantry:  # 步兵
            distance = 10
        else:  # 车辆
            distance = 25
    if tar_loc_probs.size != n_tar_locs:  # 等概率的情况
        denominator = n_tar_locs
        if denominator == 0:
            return np.zeros(n_obj_locs)  # 没有敌人时，没有暴露
        unhidden_ratio = np.sum((see_matrix[obj_locs][:, tar_locs]) &
                                (dist_matrix[obj_locs][:, tar_locs] <= distance) &
                                (dist_matrix[obj_locs][:, tar_locs] > 0),
                                axis=1) / denominator
    else:  # 不等概率的情况
        denominator = np.sum(tar_loc_probs)
        if denominator == 0:
            return np.zeros(n_obj_locs)  # 没有敌人时，没有暴露
        unhidden_ratio = np.sum(
            ((see_matrix[obj_locs][:, tar_locs]) &
             (dist_matrix[obj_locs][:, tar_locs] <= distance) &
             (dist_matrix[obj_locs][:, tar_locs] > 0)) *
            np.tile(tar_loc_probs, (n_obj_locs, 1)),
            axis=1) / denominator
    return unhidden_ratio


def calc_fire_effect(obj, cur_hex, tar_area, distance, see_matrix, dist_matrix):
    """ 2020-5-20 待进一步优化
    计算算子在cur_hex对目标区域的攻击效能
    """
    fire_effect = np.sum((see_matrix[cur_hex][:, tar_area]) &
                         (dist_matrix[cur_hex][:, tar_area] <= distance) &
                         (dist_matrix[cur_hex][:, tar_area] > 0),
                         axis=1) / len(tar_area)
    return fire_effect


def gen_infantry_route(coord1, coord2, myAgent):
    """
    2020-6-29
    针对步兵，生成从起始点到目标点的最短路径
    :param coord1:出发点
    :param coord2: 目标点
    :param myAgent:
    :return: 消耗最少且路径最短的path，path[0]是coord1的下一格
    """
    min_cost, shortest_path = astar(coord1, coord2, myAgent)
    return shortest_path


